#include <stdio.h>
#include <stdlib.h>
//#include <pthread.h>

#include "general.h"
#include "stack.h"

extern Statistics stat;

Stack newStack(void)
{
  Stack new;
  new = calloc(1, sizeof(Stack_T));
  if (new == NULL)
    {
      fprintf(stderr, "newStack() unable to allocate memory!\n");
    }
  new->top = 0;
  new->num = 0;
  return new;
}

void delStack(Stack s)
{
  free(s);
}

int isStackEmpty(Stack s)
{
  if (s->top == 0) return 1;
  else return 0;
}

void push(Stack s, StackElement e)
{
  int top;
  s->num++;
  stat.deq_avg += s->num;
  stat.deq_avg /= 2;

  //printf("stack %d\n", s->num);

  while (1)			/* blocking until pushed */
    {
      top = s->top;
      if (incStackPtr(top) >= STACK_DEPTH) continue; /* no more space */
      if (top < 0) continue;
      if (__sync_bool_compare_and_swap(&(s->top), top, -1))
      	{
	  s->stack[top].element = e->element;
	  while(!__sync_bool_compare_and_swap(&(s->top), -1, incStackPtr(top)));
	  return;		/* push successful */
      	}
    }
}

void pop(Stack s, StackElement e)
{
  int top;

  stat.dequeues++;

  while (1)
    {
      top = s->top;
      if (top <= 0) 
	{
	  stat.dequeue_wait++;
	  continue; /* nothing to pop */
	}
      if (__sync_bool_compare_and_swap(&(s->top), top, -1))
      	{
	  top = decStackPtr(top); 
      	  e->element = s->stack[top].element;
      	  s->stack[top].element = NULL;
	  while(!__sync_bool_compare_and_swap(&(s->top), -1, top));
	  s->num--;
      	  return;
      	}
    }
}

int nbpop(Stack s, StackElement e)
{
  int top;

  stat.dequeues++;

  while (1)
    {
      top = s->top;
      if (top <= 0) 
	{
	  stat.dequeue_wait++;
	  return 0; /* nothing to pop */
	}
      if (__sync_bool_compare_and_swap(&(s->top), top, -1))
      	{
	  top = decStackPtr(top); 
      	  e->element = s->stack[top].element;
      	  s->stack[top].element = NULL;
	  while(!__sync_bool_compare_and_swap(&(s->top), -1, top));
	  s->num--;
      	  return 1;
      	}
    }
}

/* Stack f; */
/* pthread_barrier_t bar; */

/* void * tester (void * t) */
/* { */
/*   long tid = (long) t; */
/*   long i; */
/*   StackElement_T d; */

/*   pthread_barrier_wait(&bar); */

/*   for (i = 0; i < 2; i++) */
/*     { */
/*       d.element = (void*) ((long)(tid*1000+i)); */
/*       push(f, &d); */
/*     } */

/*   for (i = 0; i < 2; i++) */
/*     { */
/*       pop(f, &d); */
/*       printf("%ld %ld\n", tid, (long) d.element); */
/*     } */

/*   pthread_barrier_wait(&bar); */
/*   pthread_exit(NULL); */
/* } */

/* int main(int argc, char * argv[]) */
/* { */
/*   pthread_t threads[16]; */
/*   void *status; */
/*   int rc; */
/*   long t; */
/*   int i; */


/*   f = newStack(); */

/*   pthread_barrier_init(&bar, NULL, atoi(argv[1])); */

/*   for(t=0; t<atoi(argv[1]); t++) */
/*     { */
/*       //printf("In main: creating thread %ld\n", t); */
/*       rc = pthread_create(&threads[t], NULL, tester, (void *)t); */
/*       if (rc) */
/* 	{ */
/* 	  printf("ERROR; return code from pthread_create() is %d\n", rc); */
/* 	  exit(-1); */
/* 	} */
/*     } */


/*   for(t=0; t<atoi(argv[1]); t++) */
/*     { */
/*       rc = pthread_join(threads[t], &status); */
/*       if (rc) */
/* 	{ */
/* 	  printf("ERROR; return code from pthread_join() is %d\n", rc); */
/* 	  exit(-1); */
/* 	} */
/*       //printf("Main: completed join with thread %ld having a status of %ld\n",t,(long)status); */
/*     } */

/*   pthread_barrier_destroy(&bar); */
/*   pthread_exit(NULL); */
/* } */
